//
// itertool - a polynormial and type-safe iterator framework in C++.
//
// Copyright (c) 2021-2025 Feng Cong
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT

#include <itertool/itertool_fwd.h>

#include <cmath>

#include "itertool_test.h"

namespace {
class MathIter : public itertool::Iterable<double> {
 public:
  using ItemType = double;
  explicit MathIter(size_t N) : N_(N) {}
  bool HasNext() override { return now_ < N_; }
  double GetNow() override { return ::log(now_); }
  void Advance() override { now_++; }
  bool IsFinite() override { return true; }

 private:
  long now_ = 0;
  size_t N_;
};

}  // namespace

TEST_CASE("Implement a subclass of Iterable") {
  itertool::Iterable<double>* iter = new MathIter(10);
  CHECK(iter->IsFinite());
  CHECK_FALSE(iter->IsResetable());

  for (;iter->HasNext();iter->Advance()) {
    double val = iter->GetNow();
  }
  delete iter;
}

TEST_CASE("Iterator is a wrapper around Iterable") {
  const size_t N = 10;
  itertool::Iterator<double> it = itertool::MakeIterator<MathIter>(N);

  SUBCASE("Basic method forwarding") {
    CHECK(it.IsFinite());
    CHECK_FALSE(it.IsResetable());
    CHECK(std::is_same<double, decltype(it)::ItemType>::value);
    for (;it.HasNext();it.Advance()) {
      it.GetNow();
    }
  }

  SUBCASE("Iterator should be copy-constructible and copy-assignable") {
    itertool::Iterator<double> other(it);
    other = it;
  }

  SUBCASE("Iterator::iterator has proper properties") {
    auto begin = it.begin(), end = it.end();
    using type = decltype(begin);
    using traits = std::iterator_traits<type>;
    static_assert(
        std::is_same<typename traits::reference, decltype(it)::ItemType>::value,
        "iterator::reference should equal to Iterator::ItemType");
  }

  SUBCASE("Iterator::iterator support range-based loop") {
    int i = 0;
    for (double val : it) {
      ++i;
    }
    CHECK_EQ(i, N);
  }
}
